<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Markdown Editor</title>
    <link rel="stylesheet" href="codemirror/lib/codemirror.css">
    <link rel="stylesheet" href="custom/css/github-markdown.min.css">
    <link rel="stylesheet" href="custom/css/github.min.css">
    <link rel="stylesheet" href="custom/css/primer.css">
    <link rel="stylesheet" href="custom/css/primer-primitives.css">
    <link rel="stylesheet" href="custom/css/katex.min.css">
    <link rel="stylesheet" href="custom/css/icon.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <script src="codemirror/lib/codemirror.js"></script>
    <script src="codemirror/mode/markdown/markdown.js"></script>
    <script src="custom/js/marked.min.js"></script>
    <script src="custom/js/highlight.min.js"></script>
    <script src="custom/js/highlightjs-line-numbers.min.js"></script>
    <script src="custom/js/katex.min.js"></script>
    <script src="custom/js/auto-render.min.js"></script>
    <script src="custom/js/mermaid.min.js"></script>
    <script src="custom/js/purify.min.js"></script>
    <style>
        :root {
            --color-background: #f6f8fa;
            --color-border: #e1e4e8;
            --color-text: #212529;
        }

        *,
        *::before,
        *::after {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html,
        body {
            height: 100%;
            width: 100%;
            min-height: 100vh;
            min-width: 100vw;
            overflow: hidden;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
            color: var(--color-text);
            background-color: var(--color-background);
            height: 100vh;
            width: 100vw;
            overflow: hidden;
        }

        .container {
            display: flex;
            flex-direction: column;
            height: 100vh;
            width: 100vw;
        }

        header {
            flex: 0 0 auto;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 5px 20px;
            border-bottom: 1px solid var(--color-border);
            user-select: none;
            background: #fff;
            z-index: 10;
        }

        header h1 {
            font-size: 18px;
            white-space: nowrap;
        }

        main {
            flex: 1 1 0;
            display: flex;
            flex-direction: row;
            background: #fff;
            overflow: hidden;
            min-height: 0;
            min-width: 0;
        }

        ul {
            list-style: none;
        }

        .nav {
            display: flex;
            font-size: 14px;
            gap: 8px;
        }

        .nav-item {
            position: relative;
            padding: 0.4rem 0.8rem;
            cursor: pointer;
            border-radius: 4px;
            transition: background 0.2s;
            white-space: nowrap;
        }

        .nav-item:hover,
        .nav-item:focus-within {
            background-color: #e9ecef;
        }

        .nav-item:hover .dropdown-menu,
        .nav-item:focus-within .dropdown-menu {
            display: block;
            background: #e9ecef;
        }

        .dropdown-menu {
            display: none;
            position: absolute;
            top: 100%;
            right: 0;
            min-width: 150px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            border-radius: 6px;
            animation: slideDown 0.3s ease;
            z-index: 100;
            background: #fff;
            border: 1px solid var(--color-border);
        }

        .dropdown-menu.active {
            display: block;
        }

        button {
            background-color: transparent;
            border: none;
            cursor: pointer;
        }

        .menu-item {
            position: relative;
            padding: 0.3rem 1rem 0.3rem 1.3rem;
            color: #212529;
            transition: all 0.2s;
            border-radius: 4px;
            border-bottom: 1px solid #f1f3f5;
            background: none;
            text-align: left;
            width: 100%;
            display: block;
        }

        .menu-item::before {
            content: '';
            position: absolute;
            left: 12px;
            top: 50%;
            transform: translateY(-50%);
            width: 2px;
            height: 16px;
            background: #0366d6;
            border-radius: 1px;
            opacity: 0.6;
            transition: opacity 0.2s;
        }

        .menu-item:hover::before {
            background: #d6036c;
        }

        .menu-item:last-child {
            border-bottom: none;
        }

        .menu-item:hover {
            background: #f6f8fa;
        }

        .editor-panel {
            display: flex;
            flex-direction: column;
            flex: 1 1 0;
            min-width: 0;
            min-height: 0;
            background: #fff;
        }

        .toolbar {
            --toolbar-bg: rgba(255, 255, 255, 0.95);
            --toolbar-border: rgba(0, 0, 0, 0.08);
            --toolbar-shadow: 0 12px 32px rgba(0, 0, 0, 0.1);
            --button-color: #4b5563;
            --button-hover-bg: rgba(59, 130, 246, 0.1);
            --button-hover-color: #3b82f6;
            --button-active-bg: rgba(59, 130, 246, 0.2);
            --divider-color: rgba(0, 0, 0, 0.08);
            --tooltip-bg: #1f2937;
            --tooltip-color: #f9fafb;

            display: flex;
            background: var(--toolbar-bg);
            box-shadow: var(--toolbar-shadow);
            backdrop-filter: blur(8px);
            transition: all 0.3s ease;
        }

        .toolbar button {
            position: relative;
            padding: 4px;
            border: none;
            background: none;
            border-radius: 8px;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 8px;
            color: var(--button-color);
            font-size: 14px;
            font-weight: 500;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            overflow: hidden;
        }

        .toolbar button::before {
            font-family: 'Material Icons';
            font-size: 20px;
            font-weight: normal;
            display: inline-block;
            line-height: 1;
            vertical-align: middle;
            transition: transform 0.2s ease;
        }

        button[data-action="bold"]::before {
            content: "format_bold";
        }

        button[data-action="italic"]::before {
            content: "format_italic";
        }

        button[data-action="heading"]::before {
            content: "title";
        }

        button[data-action="quote"]::before {
            content: "format_quote";
        }

        button[data-action="code"]::before {
            content: "code";
        }

        button[data-action="link"]::before {
            content: "link";
        }

        button[data-action="image"]::before {
            content: "image";
        }

        button[data-action="ul"]::before {
            content: "format_list_bulleted";
        }

        button[data-action="ol"]::before {
            content: "format_list_numbered";
        }

        button[data-action="table"]::before {
            content: "table_chart";
        }

        button[data-action="hr"]::before {
            content: "horizontal_rule";
        }

        .toolbar button:hover::after {
            opacity: 1;
            bottom: -38px;
        }

        .toolbar-divider {
            width: 1px;
            background: var(--divider-color);
            margin: 2px 8px;
            align-self: stretch;
        }

        #code {
            flex: 1 1 0;
            min-height: 0;
            width: 100%;
            font-size: 14px;
            outline: none;
            resize: none;
            padding: 10px 16px;
            box-sizing: border-box;
            background: #fff;
            color: var(--color-text);
            font-family: inherit;
        }

        .CodeMirror {
            flex: 1 1 0;
            min-width: 0;
            min-height: 0;
            height: 100%;
            font-size: 14px;
            z-index: 1;
            border-top: 1px solid var(--color-border);
        }

        #preview {
            flex: 1 1 0;
            min-width: 0;
            min-height: 0;
            background: #fafbfc;
            padding: 10px 16px;
            overflow-y: auto;
            border-left: 1px solid var(--color-border);
            line-height: 1.6;
            z-index: 1;
        }

        .katex {
            font-size: 1.1em;
        }

        @media (max-width: 800px) {
            main {
                flex-direction: column;
            }

            .CodeMirror {
                min-width: 0;
                border-right: none;
                border-bottom: 2px solid var(--color-border);
                height: 50vh;
            }

            #preview {
                min-width: 0;
                height: 50vh;
                display: block;
            }

            .toolbar {
                position: static;
                top: auto;
                left: auto;
                right: auto;
            }
        }
    </style>
</head>

<body>
    <div class="container">
        <header>
            <h1>在线 Markdown 编辑器</h1>
            <ul class="nav">
                <li class="nav-item">
                    <button>示例文件</button>
                    <ul class="dropdown-menu" role="menu" id="examplesMenu"></ul>
                </li>
                <li class="nav-item" id="newFile">
                    <button>新建文件</button>
                </li>
                <li class="nav-item" id="openFile">
                    <button>打开文件</button>
                </li>
                <li class="nav-item">
                    <button>保存文件</button>
                    <ul class="dropdown-menu" role="menu" id="saveMenu">
                        <li class="menu-item" id="saveHtml" data-type="html">HTML 格式</li>
                        <li class="menu-item" id="saveMarkdown" data-type="markdown">Markdown 格式</li>
                    </ul>
                </li>
                <li class="nav-item">
                    <button>参考手册</button>
                    <ul class="dropdown-menu" role="menu" id="manualMenu">
                        <li class="menu-item" data-url="https://www.runoob.com/markdown/md-tutorial.html">菜鸟教程</li>
                        <li class="menu-item" data-url="https://www.markdown.cn/">Markdown 中文</li>
                    </ul>
                </li>
            </ul>
        </header>
        <main>
            <div class="editor-panel">
                <div class="toolbar" id="toolbar">
                    <button type="button" data-action="bold"></button>
                    <button type="button" data-action="italic"></button>
                    <span class="toolbar-divider"></span>
                    <button type="button" data-action="heading"></button>
                    <button type="button" data-action="quote"></button>
                    <span class="toolbar-divider"></span>
                    <button type="button" data-action="code"></button>
                    <button type="button" data-action="link"></button>
                    <button type="button" data-action="image"></button>
                    <span class="toolbar-divider"></span>
                    <button type="button" data-action="ul"></button>
                    <button type="button" data-action="ol"></button>
                    <span class="toolbar-divider"></span>
                    <button type="button" data-action="table"></button>
                    <button type="button" data-action="hr"></button>
                </div>
                <textarea id="code" name="code" placeholder="在此输入Markdown内容..."></textarea>
            </div>
            <div id="preview" class="markdown-body"></div>
        </main>
        <!-- TOC功能已移除 -->
    </div>

    <script>
        // 工具函数
        const throttle = (fn, delay) => {
            let timer = null;
            return function (...args) {
                if (timer) return;
                timer = setTimeout(() => {
                    fn.apply(this, args);
                    timer = null;
                }, delay);
            };
        };

        // 编辑器初始化
        const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
            mode: "markdown",
            theme: 'default',
            lineNumbers: true,
            indentWithTabs: false,
            tabSize: 4,
            indentUnit: 4,
            smartIndent: true,
            autofocus: true,
            viewportMargin: Infinity,
            extraKeys: {
                "Tab": cm => cm.replaceSelection("    "),
            }
        });

        // marked 配置
        marked.use({
            gfm: true,
            breaks: true,
            headerIds: true,
            highlight: (code, lang) => {
                if (lang === 'mermaid') return code;
                const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
                return hljs.highlight(code, { language: validLang }).value;
            },
            hooks: {
                postprocess: html => DOMPurify.sanitize(html)
            },
            extensions: [
                {
                    name: 'math',
                    level: 'block',
                    start(src) { return src.indexOf('$$'); },
                    tokenizer(src) {
                        const match = src.match(/^\$\$([\s\S]*?)\$\$/);
                        if (match) {
                            return {
                                type: 'math',
                                raw: match[0],
                                text: match[1].trim()
                            };
                        }
                    },
                    renderer: token => katex.renderToString(token.text)
                },
                {
                    name: 'mermaid',
                    level: 'block',
                    start(src) { return src.match(/^```mermaid/m)?.index; },
                    tokenizer(src) {
                        const match = src.match(/^```mermaid\s*([\s\S]+?)```/);
                        if (match) {
                            return {
                                type: 'mermaid',
                                raw: match[0],
                                text: match[1].trim()
                            };
                        }
                    },
                    renderer(token) {
                        return `<div class="mermaid">${token.text}</div>`;
                    }
                }
            ]
        });

        // 工具栏功能
        const toolbarActions = {
            bold: () => insertAtCursor('**', '**', '加粗文本'),
            italic: () => insertAtCursor('*', '*', '斜体文本'),
            heading: () => insertLineAtCursor('# 标题'),
            quote: () => insertLineAtCursor('> '),
            code: () => insertAtCursor('\n```language\n', '\n```\n', '代码内容'),
            link: () => {
                const url = prompt('请输入链接地址：', 'https://');
                if (url) insertAtCursor('[', `](${url})`, '链接文本');
            },
            image: () => {
                const url = prompt('请输入图片链接：', 'https://');
                if (url) insertAtCursor('![图片描述](', ')', url);
            },
            ul: () => insertList(false),
            ol: () => insertList(true),
            table: () => insertLineAtCursor('| 表头1 | 表头2 |\n| --- | --- |\n| 内容1 | 内容2 |'),
            hr: () => insertLineAtCursor('---')
        };

        // 工具栏按钮提示文本
        const toolbarTips = {
            bold: '加粗',
            italic: '斜体',
            heading: '标题',
            quote: '引用',
            code: '代码块',
            link: '插入链接',
            image: '插入图片',
            ul: '无序列表',
            ol: '有序列表',
            table: '插入表格',
            hr: '分割线'
        };

        // 给工具栏按钮添加 title 属性
        document.querySelectorAll('#toolbar button[data-action]').forEach(btn => {
            const action = btn.dataset.action;
            if (toolbarTips[action]) {
                btn.title = toolbarTips[action];
            }
        });

        document.getElementById('toolbar').addEventListener('click', e => {
            const btn = e.target.closest('button[data-action]');
            if (!btn) return;
            const action = btn.dataset.action;
            if (toolbarActions[action]) {
                toolbarActions[action]();
            }
        });

        function insertAtCursor(before, after, placeholder = '') {
            const doc = editor.getDoc();
            const sel = doc.getSelection();
            const text = sel || placeholder;
            doc.replaceSelection(before + text + after);
            if (!sel && placeholder) {
                const cursor = doc.getCursor();
                doc.setSelection(
                    { line: cursor.line, ch: cursor.ch - after.length - placeholder.length },
                    { line: cursor.line, ch: cursor.ch - after.length }
                );
            }
            editor.focus();
        }

        function insertLineAtCursor(lineText) {
            const doc = editor.getDoc();
            const cursor = doc.getCursor();
            doc.replaceRange(lineText + '\n', { line: cursor.line, ch: 0 });
            editor.focus();
        }

        function insertList(ordered = false) {
            const doc = editor.getDoc();
            const sel = doc.getSelection();
            const lines = sel ? sel.split('\n') : ['列表项'];
            const prefix = ordered ? (i) => `${i + 1}. ` : () => '- ';
            const newText = lines.map((line, i) => prefix(i) + (line.trim() || '列表项')).join('\n');
            doc.replaceSelection(newText);
            editor.focus();
        }

        // 实时预览
        const updatePreview = throttle(() => {
            const content = editor.getValue();
            document.getElementById('preview').innerHTML = marked.parse(content);
            renderMath();
            renderMermaid();
            hljs.highlightAll();
        }, 200);

        editor.on('change', updatePreview);

        // Mermaid 初始化
        mermaid.initialize({
            startOnLoad: false,
            theme: 'dark',
            flowchart: {
                curve: 'basis',
                htmlLabels: true
            },
            sequence: {
                diagramMarginX: 50,
                diagramMarginY: 10
            }
        });

        function renderMermaid() {
            document.querySelectorAll('.mermaid').forEach(async (el, index) => {
                try {
                    const { svg } = await mermaid.mermaidAPI.render(`mermaid-${Date.now()}-${index}`, el.textContent);
                    el.innerHTML = svg;
                } catch (error) {
                    el.innerHTML = `<div class="mermaid-error">流程图渲染错误: ${error.message}</div>`;
                }
            });
        }

        // 数学公式渲染
        function renderMath() {
            if (typeof renderMathInElement === 'function') {
                renderMathInElement(document.getElementById('preview'), {
                    delimiters: [
                        { left: '$$', right: '$$', display: true },
                        { left: '$', right: '$', display: false }
                    ]
                });
            }
        }

        // 示例菜单
        const examples = {
            demo: {
                title: "示例",
                url: "custom/examples/demo.md"
            },
        };
        const examplesMenu = document.getElementById('examplesMenu');
        Object.entries(examples).forEach(([key, example]) => {
            const li = document.createElement('li');
            li.className = 'menu-item';
            li.textContent = example.title;
            li.dataset.exampleKey = key;
            examplesMenu.appendChild(li);
        });
        examplesMenu.addEventListener('click', (event) => {
            const exampleKey = event.target.dataset.exampleKey;
            if (exampleKey && examples[exampleKey]) {
                fetch(examples[exampleKey].url)
                    .then(response => response.text())
                    .then(html => {
                        editor.setValue(html);
                        updatePreview();
                    })
                    .catch(error => console.error('Error loading example:', error));
            }
        });

        // 参考手册菜单
        document.getElementById('manualMenu').addEventListener('click', (event) => {
            const url = event.target.dataset.url;
            if (url) {
                window.open(url, '_blank');
            }
        });

        // 新建文件
        document.getElementById('newFile').addEventListener('click', () => {
            if (editor.getValue() && !confirm('当前内容未保存，确定要新建吗？')) return;
            editor.setValue('');
            localStorage.removeItem('markdown-content');
            updatePreview();
        });

        // 打开文件
        document.getElementById('openFile').addEventListener('click', () => {
            const input = document.createElement('input');
            input.type = 'file';
            input.accept = '.md';
            input.onchange = e => {
                const file = e.target.files[0];
                if (!file) return;
                const reader = new FileReader();
                reader.onload = () => {
                    editor.setValue(reader.result);
                    updatePreview();
                };
                reader.readAsText(file);
            };
            input.click();
        });

        // 保存文件
        function saveFile(type) {
            let content, filename, mimeType;
            if (type === 'html') {
                content = document.getElementById('preview').innerHTML;
                filename = `document-${Date.now()}.html`;
                mimeType = 'text/html';
            } else {
                content = editor.getValue();
                filename = `document-${Date.now()}.md`;
                mimeType = 'text/markdown';
            }
            const blob = new Blob([content], { type: mimeType });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            a.click();
            URL.revokeObjectURL(url);
        }
        document.getElementById('saveHtml').onclick = () => saveFile('html');
        document.getElementById('saveMarkdown').onclick = () => saveFile('markdown');

        // 图片粘贴上传
        editor.getWrapperElement().addEventListener('paste', async function (e) {
            const items = e.clipboardData.items;

            for (let item of items) {
                if (item.type.indexOf('image') === 0) {
                    const file = item.getAsFile();
                    const compressedFile = await compressImage(file);
                    const reader = new FileReader();

                    reader.onload = (evt) => {
                        const imgMarkdown = `![${formatDate(new Date())}](${evt.target.result})`;
                        editor.replaceSelection(`\n${imgMarkdown}\n`);
                    };

                    reader.readAsDataURL(compressedFile);
                }
            }
        });

        // 图片压缩函数
        async function compressImage(file, maxWidth = 800, quality = 0.8) {
            return new Promise((resolve) => {
                const img = new Image();
                img.src = URL.createObjectURL(file);

                img.onload = () => {
                    let width = img.width;
                    let height = img.height;
                    if (width > maxWidth) {
                        const scale = maxWidth / width;
                        width = maxWidth;
                        height = height * scale;
                    }
                    const canvas = document.createElement('canvas');
                    canvas.width = width;
                    canvas.height = height;

                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                    canvas.toBlob(
                        (blob) => resolve(new File([blob], file.name, { type: 'image/jpeg' })),
                        'image/jpeg',
                        quality
                    );
                };
            });
        }

        // 文件拖放上传
        editor.getWrapperElement().addEventListener('drop', function (e) {
            e.preventDefault();
            const files = e.dataTransfer.files;
            if (files && files.length) {
                for (let i = 0; i < files.length; i++) {
                    const file = files[i];
                    if (file.type === 'text/markdown' || file.name.endsWith('.md')) {
                        if (editor.getValue() && !confirm('当前内容未保存，确定要替换吗？')) continue;
                        const reader = new FileReader();
                        reader.onload = function (evt) {
                            editor.setValue(evt.target.result);
                            updatePreview();
                        };
                        reader.readAsText(file);
                        break;
                    } else if (file.type.indexOf('image') !== -1) {
                        const reader = new FileReader();
                        reader.onload = function (evt) {
                            const imgMarkdown = `\n![${file.name}](${evt.target.result})\n`;
                            editor.replaceSelection(imgMarkdown);
                        };
                        reader.readAsDataURL(file);
                    }
                }
            }
        });

        // 滚动同步
        let syncing = false;
        const preview = document.getElementById('preview');
        editor.on('scroll', (cm) => {
            if (syncing) return;
            syncing = true;
            const scrollInfo = cm.getScrollInfo();
            const ratio = scrollInfo.top / (scrollInfo.height - scrollInfo.clientHeight);
            preview.scrollTop = ratio * (preview.scrollHeight - preview.clientHeight);
            syncing = false;
        });
        preview.addEventListener('scroll', () => {
            if (syncing) return;
            syncing = true;
            const ratio = preview.scrollTop / (preview.scrollHeight - preview.clientHeight);
            editor.scrollTo(null, ratio * (editor.getScrollInfo().height - editor.getScrollInfo().clientHeight));
            syncing = false;
        });

        // 自动保存
        setInterval(() => {
            localStorage.setItem('markdown-content', editor.getValue());
        }, 10000);

        // 启动时恢复
        window.addEventListener('DOMContentLoaded', () => {
            const saved = localStorage.getItem('markdown-content');
            if (saved) editor.setValue(saved);
            updatePreview();
        });

        // 补充：格式化日期函数，防止图片名为 undefined
        function formatDate(date) {
            return date.getFullYear() + '-' +
                String(date.getMonth() + 1).padStart(2, '0') + '-' +
                String(date.getDate()).padStart(2, '0') + ' ' +
                String(date.getHours()).padStart(2, '0') + ':' +
                String(date.getMinutes()).padStart(2, '0') + ':' +
                String(date.getSeconds()).padStart(2, '0');
        }
    </script>
</body>

</html>